package com.quick.system.service.impl;

import com.quick.common.constants.CacheConstants;
import com.quick.common.constants.Constants;
import com.quick.common.constants.UserConstants;
import com.quick.common.core.domain.LoginUser;
import com.quick.common.exception.ServiceException;
import com.quick.common.redis.RedisCache;
import com.quick.common.utils.ServletUtils;
import com.quick.common.utils.StringUtils;
import com.quick.common.utils.ip.AddressUtils;
import com.quick.common.utils.ip.IpUtils;
import com.quick.framework.manager.AsyncManager;
import com.quick.framework.security.context.AuthenticationContextHolder;
import com.quick.framework.service.TokenService;
import com.quick.framework.service.UserLoginService;
import com.quick.system.domain.SysDept;
import com.quick.system.domain.SysRole;
import com.quick.system.domain.SysUser;
import com.quick.system.domain.dto.LoginDTO;
import com.quick.system.domain.vo.LoginVo;
import com.quick.system.manager.AsyncLogService;
import com.quick.system.mapper.SysDeptMapper;
import com.quick.system.service.*;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author: Administrator
 * @Date: 2023/7/26
 */
@Slf4j
@Service
public class SysLoginServiceImpl implements SysLoginService, UserLoginService {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private SysDeptMapper sysDeptMapper;

    /**
     * 登录
     *
     * @param loginDTO 登录信息
     * @return 登录响应
     */
    @Override
    public LoginVo login(LoginDTO loginDTO) {
        String message = "登录失败！";
        String status = Constants.LOGIN_FAIL;
        try {
            //验证码验证
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + loginDTO.getUuid();
            String code = redisCache.getCacheObject(verifyKey);
            if (StringUtils.isEmpty(code)) {
                message = "验证码已失效！";
                throw new ServiceException(message);
            }
            if (!code.equalsIgnoreCase(loginDTO.getCode())) {
                message = "验证码错误！";
                throw new ServiceException(message);
            }
            // 用户验证
            Authentication authentication;
            try {
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword());
                AuthenticationContextHolder.setContext(authenticationToken);
                authentication = authenticationManager.authenticate(authenticationToken);
            } catch (Exception e) {
                if (e instanceof BadCredentialsException) {
                    message = "账号密码不匹配！";
                    throw new ServiceException(message);
                }
                if (e instanceof InternalAuthenticationServiceException) {
                    message = e.getMessage();
                    throw new ServiceException(message);
                } else {
                    throw new ServiceException(message, e);
                }
            }
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            //记录登录信息
            recordLoginInfo(loginUser.getUserId());
            status = Constants.LOGIN_SUCCESS;
            message = "登录成功！";
            return new LoginVo(tokenService.createToken(loginUser), loginUser.getExpireTime());
        } finally {
            AuthenticationContextHolder.clearContext();
            AsyncManager.getInstance().execute(AsyncLogService.recordLoginInfo(loginDTO.getUsername(), status, message));
        }
    }


    /**
     * 查询用户信息
     *
     * @param userName 用户账号
     * @return 用户信息
     */
    @Override
    public LoginUser selectSysUserByUserName(String userName) {
        SysUser sysUser = sysUserService.selectSysUserByUserName(userName);
        if (Objects.isNull(sysUser)) {
            throw new InternalAuthenticationServiceException(StringUtils.format("账号{}不存在！", userName));
        } else if (UserConstants.USER_DELETE.equals(sysUser.getDelFlag())) {
            throw new InternalAuthenticationServiceException(StringUtils.format("账号{}已删除！", userName));
        } else if (UserConstants.USER_DISABLE.equals(sysUser.getStatus())) {
            throw new InternalAuthenticationServiceException(StringUtils.format("账号{}已停用！", userName));
        }
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(sysUser.getUserId());
        loginUser.setDeptId(sysUser.getDeptId());
        loginUser.setUserName(sysUser.getUserName());
        loginUser.setPassword(sysUser.getPassword());
        loginUser.setLoginTime(System.currentTimeMillis());
        SysDept sysDept = sysDeptMapper.selectSysDeptByDeptId(sysUser.getDeptId());
        if (Objects.nonNull(sysDept)) {
            loginUser.setDeptName(sysDept.getDeptName());
        }
        return loginUser;
    }

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    @Override
    public void setUserAgent(LoginUser loginUser) {
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIp(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());
        loginUser.setLoginTime(System.currentTimeMillis());
    }

    /**
     * 设置权限
     *
     * @param loginUser 登录用户
     */
    @Override
    public void setPermission(LoginUser loginUser) {
        List<SysRole> sysRoles = sysRoleService.selectSysRoleByUserId(loginUser.getUserId());
        Set<String> perms = sysMenuService.selectSysMenuPermsByUserId(loginUser.getUserId());
        Set<LoginUser.Role> roles = new HashSet<>();
        for (SysRole sysRole : sysRoles) {
            LoginUser.Role role = new LoginUser.Role();
            role.setRoleId(sysRole.getRoleId());
            role.setDataScope(sysRole.getDataScope());
            role.setRoleKey(sysRole.getRoleKey());
            roles.add(role);
        }
        loginUser.setRoles(roles);
        loginUser.setPermissions(perms);
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    private void recordLoginInfo(Long userId) {
        sysUserService.updateRecordLoginInfo(userId);
    }
}